package general.CPA;


import algorithms.TwoIntegers;

public abstract class CPA implements Iterable<Integer>{
	
	private int steps;
	private int creator;
	private int id;
	
	/**
	 * FOR PROTOTYPE ONLY
	 */
	protected CPA() {
	}
	
	/**
	 * Creates an instance of CPA
	 * @param creator id of the CPA's creator
	 * @param id id of the CPA
	 * @param steps number of steps for this CPA
	 */
	protected CPA(int id, int creator, int steps){
		this.creator = creator;
		this.id = id;
		this.steps = steps;
	}
	/**
	 * insert an assignment into this CPA
	 * @param var the variable to assign a value to
	 * @param value the value
	 */
	public abstract void insert(byte var, byte value);
	/**
	 * Checks if a variable is already assigned in a CPA
	 * @param i index of the variable to check
	 * @return True - if the variable has a value in this CPA, False - otherwise
	 */
	public abstract boolean isAssigned(byte i);
	/**
	 * Retrieves the value of a variable in this CPA
	 * @param i the index of the variable
	 * @return The value associated with the given variable - or -1 if the variable is not assigned
	 */
	public abstract byte getValue(byte i);
//	/**
//	 * Empties this CPA, and resetting the number of steps
//	 */
//	public abstract void reset();
	/**
	 * Removes a variable assignment from this CPA
	 * @param i the index of the variable
	 */
	public abstract void remove(byte i);
	/**
	 * Used to serialize this CPA for sending through the web
	 * @return a byte array representing this CPA
	 */
	public abstract byte[] getBytes();
	
	//TODO
	public abstract int byteSize();
	
	//TODO
	public abstract byte getType();
	
	/**
	 * Get the CPA's ID 
	 * @return the ID of this CPA itself
	 */
	protected int getCpaId(){
		return id;
	}
	/**
	 * Checks if all of the variables are assigned in this CPA
	 * @return True if the CPA contains a complete variable assignment
	 */
	public abstract boolean isFull();
	/**
	 * Get the creator of this CPA
	 * @return the ID of the creator of this CPA
	 */
	public int getCreator(){
		return creator;
	}
	/**
	 * Resets the number of steps
	 */
	public void resetSteps() {
		steps=0;
	}
	/**
	 * Adds 1 step to the step counter in this CPA
	 */
	public void addStep() {
		steps++;
	}
	/**
	 * Retrieves the number of steps that were done in the CPA before coming back to its creator
	 * @return the number of steps in this CPA that were done before returning to its creator 
	 */
	public int getSteps() {
		return steps;
	}
	
	/**
	 * Gets this CPA's ID - which is a unique object containing the CPA's ID (which is the chrono
	 * @return
	 */
	public TwoIntegers getId(){
		return new TwoIntegers(id, creator);
	}
	
	/**
	 * Duplicates the CPA, with 0 steps and the new ID/Creator
	 * @param id ID for the new CPA
	 * @param creator creator for the new CPA
	 * @return the generated CPA
	 */
	public abstract CPA duplicate(int id, byte creator);
	
	/**
	 * used to duplicate a CPA prototype
	 * @param n total number of agents in the system
	 * @param cpaId ID of the CPA
	 * @param creator Creator of the CPA
	 * @return a new Instance of CPA using the given values
	 */
	public abstract CPA createNew(int n, int cpaId, int creator);
	
	/**
	 * used to duplicate a CPA prototype (mainly used after receiving a CPA message through the network
	 * @param id ID of the CPA
	 * @param steps steps done with the CPA without going back to the creator
	 * @param creator Creator of the CPA
	 * @param cpa the assignments of this CPA as a byte array (-1 for unassigned)
	 * @return a new Instance of CPA using the given values
	 */
	public abstract CPA createNew(int id, int steps, int creator, byte[] cpa, byte[] order);
}
